#pragma once
#include <string.h>

#define TIMER_MAX_TIMEOUT (unsigned long long)~0

typedef struct ltimer_s ltimer_t;
typedef struct ltimer_s ltimer_mgr_t;
typedef void (*timer_cb)(ltimer_t* timer);
typedef void (*timer_debug_dump_cb)(ltimer_t* timer);  // only for debug
struct ltimer_s
{
    timer_cb cb;
    unsigned long long timeout;
    unsigned long long repeat;
    struct ltimer_s *prev, *next;
};

static inline void timer_mgr_init(
    ltimer_mgr_t* time_mgr, unsigned long long now_time) {
    memset(time_mgr, 0, sizeof(ltimer_mgr_t));
    time_mgr->prev = time_mgr->next = time_mgr;
    time_mgr->timeout = now_time;
}

// static void timer_mgr_debug_dump(
//     ltimer_mgr_t* timer_mgr, timer_debug_dump_cb cb) {
//     ltimer_t* timer = timer_mgr->next;
//     while (timer != timer_mgr) {
//         ltimer_t* next = timer->next;
//         cb(timer);
//         timer = next;
//     }
// }

// timeout: first trigger time
// repeat:  loop interval time after first trigger
static inline int timer_start(ltimer_mgr_t* timer_mgr, ltimer_t* timer,
    timer_cb cb, unsigned long long timeout, unsigned long long repeat) {
    if (NULL == cb) {
        return -1;
    }
    unsigned long long clamped_timeout = timer_mgr->timeout + timeout;
    if (clamped_timeout < timeout) {
        clamped_timeout = (unsigned long long)~0;
    }
    timer->cb = cb;
    timer->timeout = clamped_timeout;
    timer->repeat = repeat;
    // find first node > timeout
    ltimer_t* node = timer_mgr->next;
    while (node != timer_mgr) {
        if (timer->timeout < node->timeout) {
            break;
        }
        node = node->next;
    }
    timer->prev = node->prev;
    timer->next = node;
    timer->prev->next = timer;
    node->prev = timer;
    return 0;
}

static inline int timer_stop(ltimer_t* timer) {
    if (timer->prev && timer->next) {
        timer->prev->next = timer->next;
        timer->next->prev = timer->prev;
        timer->prev = timer->next = NULL;
    }
    return 0;
}

static inline int timer_again(ltimer_mgr_t* timer_mgr, ltimer_t* timer) {
    if (NULL == timer->cb) {
        return -1;
    }
    if (timer->repeat) {
        timer_stop(timer);
        timer_start(timer_mgr, timer, timer->cb, timer->repeat, timer->repeat);
    }
    return 0;
}

static inline int timer_tick(
    ltimer_mgr_t* timer_mgr, unsigned long long now_time) {
    timer_mgr->timeout = now_time;
    if (timer_mgr->prev == timer_mgr || timer_mgr->next == timer_mgr) {
        return 0;  // no timers
    }
    ltimer_t* timer = timer_mgr->next;
    while (timer != timer_mgr) {
        ltimer_t* next = timer->next;
        if (timer->timeout > timer_mgr->timeout) {
            break;
        }
        timer_stop(timer);
        timer_again(timer_mgr, timer);
        timer->cb(timer);
        timer = next;
    }
    return 0;
}

static inline unsigned long long timer_get_repeat(const ltimer_t* timer) {
    return timer->repeat;
}

static inline void timer_set_repeat(
    ltimer_t* timer, unsigned long long repeat) {
    timer->repeat = repeat;
}

static inline unsigned long long timer_get_due_in(
    ltimer_mgr_t* timer_mgr, const ltimer_t* timer) {
    if (timer_mgr->timeout >= timer->timeout) {
        return 0;
    }
    return timer->timeout - timer_mgr->timeout;
}